<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="generator" content="VuePress 2.0.0-beta.53" />
    <meta name="theme" content="VuePress Theme Hope" />
    <link rel="icon" href="/doc/images/favicon.ico"><title>第 1 章 权衡的艺术 | AT 的文档库</title><meta name="description" content="At 的文档库，好记性不如烂笔头！">
    <style>
      :root {
        --bg-color: #fff;
      }

      html[data-theme="dark"] {
        --bg-color: #1d2025;
      }

      html,
      body {
        background: var(--bg-color);
      }
    </style>
    <script>
      const userMode = localStorage.getItem("vuepress-theme-hope-scheme");
      const systemDarkMode =
        window.matchMedia &&
        window.matchMedia("(prefers-color-scheme: dark)").matches;

      if (userMode === "dark" || (userMode !== "light" && systemDarkMode)) {
        document.querySelector("html").setAttribute("data-theme", "dark");
      }
    </script>
    <link rel="preload" href="/doc/assets/style.c3a4c10b.css" as="style" /><link rel="stylesheet" href="/doc/assets/style.c3a4c10b.css" />
    <link rel="modulepreload" href="/doc/assets/app.41440b49.js"><link rel="modulepreload" href="/doc/assets/第 1 章.html.127b1e7c.js"><link rel="modulepreload" href="/doc/assets/_plugin-vue_export-helper.cdc0426e.js"><link rel="modulepreload" href="/doc/assets/第 1 章.html.726a0bb9.js"><link rel="prefetch" href="/doc/assets/index.html.0bcc895f.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.c8df2167.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue2.html.48e096c5.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue3.html.324e646c.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.fb0ad0e0.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章 导言.html.d6c662f8.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章 TypeScript 概述.html.e976154a.js" as="script" /><link rel="prefetch" href="/doc/assets/第 3 章 类型全解.html.e4f1b931.js" as="script" /><link rel="prefetch" href="/doc/assets/第 4 章 函数.html.701a2968.js" as="script" /><link rel="prefetch" href="/doc/assets/第 5 章 类和接口.html.8988d546.js" as="script" /><link rel="prefetch" href="/doc/assets/第 6 章 类型进阶.html.45b313e3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a9dd6465.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a3374cf8.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.c326eaaf.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.88582229.js" as="script" /><link rel="prefetch" href="/doc/assets/常见问题.html.e0f65550.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展使用.html.87cccc41.js" as="script" /><link rel="prefetch" href="/doc/assets/掌握 GIT.html.c150b234.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a3c2858b.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.9a53d157.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.101afd82.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.8224e1cd.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.54b5ca2d.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.d839cc53.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.80e4d5cc.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.9a178a04.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展知识.html.3535ab30.js" as="script" /><link rel="prefetch" href="/doc/assets/项目搭建.html.e926587b.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.881e95b0.js" as="script" /><link rel="prefetch" href="/doc/assets/安装及使用.html.ba7314a3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f9509ce0.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章.html.38f55e7d.js" as="script" /><link rel="prefetch" href="/doc/assets/Controllers.html.6b27764c.js" as="script" /><link rel="prefetch" href="/doc/assets/Module.html.3849b10b.js" as="script" /><link rel="prefetch" href="/doc/assets/Providers.html.7d2793f1.js" as="script" /><link rel="prefetch" href="/doc/assets/404.html.c5de7dd9.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.86b08b0b.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f4119260.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue2.html.caf7eb18.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue3.html.6dc23cf4.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.868f2c80.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章 导言.html.9edf000e.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章 TypeScript 概述.html.76b27e4d.js" as="script" /><link rel="prefetch" href="/doc/assets/第 3 章 类型全解.html.9ad8a3d4.js" as="script" /><link rel="prefetch" href="/doc/assets/第 4 章 函数.html.9115df00.js" as="script" /><link rel="prefetch" href="/doc/assets/第 5 章 类和接口.html.e392a0d1.js" as="script" /><link rel="prefetch" href="/doc/assets/第 6 章 类型进阶.html.1b5b864e.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.e5a75968.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.974654cf.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.ed3fa7a3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.3b9ebcaa.js" as="script" /><link rel="prefetch" href="/doc/assets/常见问题.html.b427e39a.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展使用.html.76b3dd90.js" as="script" /><link rel="prefetch" href="/doc/assets/掌握 GIT.html.724416fc.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.8b55f9ef.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.8ee968a7.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.06371751.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.6b672044.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.b200eaff.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.5135d2c4.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.557b7bf5.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.7b535b09.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展知识.html.c6b233af.js" as="script" /><link rel="prefetch" href="/doc/assets/项目搭建.html.450f0b1d.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.6483ed06.js" as="script" /><link rel="prefetch" href="/doc/assets/安装及使用.html.b4d859f3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f15cf3e9.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章.html.ea8e67e2.js" as="script" /><link rel="prefetch" href="/doc/assets/Controllers.html.029df1e7.js" as="script" /><link rel="prefetch" href="/doc/assets/Module.html.f102db2a.js" as="script" /><link rel="prefetch" href="/doc/assets/Providers.html.9bef3753.js" as="script" /><link rel="prefetch" href="/doc/assets/404.html.2cfd6b20.js" as="script" /><link rel="prefetch" href="/doc/assets/photoswipe.esm.09e03fed.js" as="script" />
  </head>
  <body>
    <div id="app"><!--[--><!--[--><!--[--><span tabindex="-1"></span><a href="#main-content" class="skip-link sr-only">Skip to content</a><!--]--><div class="theme-container has-toc"><!--[--><!--[--><header class="navbar"><div class="navbar-left"><button class="toggle-sidebar-button" title="Toggle Sidebar"><span class="icon"></span></button><!--[--><!----><!--]--><a href="/doc/" class="brand"><!----><!----><span class="site-name">AT 的文档库</span></a><!--[--><!----><!--]--></div><div class="navbar-center"><!--[--><!----><!--]--><nav class="nav-links"><div class="nav-item hide-in-mobile"><a href="/doc/" class="nav-link" aria-label="首页"><!---->首页<!----></a></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="开发问题汇总"><span class="title"><!---->开发问题汇总</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/development-question/Vue" class="nav-link" aria-label="Vue"><!---->Vue<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="系统笔记"><span class="title"><!---->系统笔记</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/sysnotes/Git/%E6%8E%8C%E6%8F%A1%20GIT.html" class="nav-link" aria-label="GIT"><!---->GIT<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/Nvm/%E5%AE%89%E8%A3%85%E5%8F%8A%E4%BD%BF%E7%94%A8.html" class="nav-link" aria-label="NVM"><!---->NVM<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/Linux/%E5%9F%BA%E7%A1%80%E6%8E%8C%E6%8F%A1.html" class="nav-link" aria-label="Linux"><!---->Linux<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/JavaScript" class="nav-link" aria-label="JavaScript"><!---->JavaScript<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/CSS" class="nav-link" aria-label="CSS"><!---->CSS<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/HTML" class="nav-link" aria-label="HTML"><!---->HTML<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="阅读笔记"><span class="title"><!---->阅读笔记</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/readbook/《Vue.js 设计与实现》/" class="nav-link" aria-label="《Vue.js 设计与实现》"><!---->《Vue.js 设计与实现》<!----></a></li><li class="dropdown-item"><a href="/doc/readbook/《TypeScript 编程》/" class="nav-link" aria-label="《TypeScript 编程》"><!---->《TypeScript 编程》<!----></a></li></ul></button></div></div></nav><!--[--><!----><!--]--></div><div class="navbar-right"><!--[--><!----><!--]--><div class="nav-item hide-in-mobile"><button id="appearance-switch"><svg xmlns="http://www.w3.org/2000/svg" class="icon auto-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="auto icon" style="display:block;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="dark icon" style="display:none;"><path d="M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon light-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="light icon" style="display:none;"><path d="M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"></path></svg></button></div><form class="search-box" role="search"><input type="search" autocomplete="off" spellcheck="false" value><!----></form><!--[--><!----><!--]--><button class="toggle-navbar-button" aria-label="Toggle Navbar" aria-expanded="false" aria-controls="nav-screen"><span class="button-container"><span class="button-top"></span><span class="button-middle"></span><span class="button-bottom"></span></span></button></div></header><!----><!--]--><!----><div class="toggle-sidebar-wrapper"><span class="arrow left"></span></div><aside class="sidebar"><!--[--><!----><!--]--><ul class="sidebar-links"><li><section class="sidebar-group"><button class="sidebar-heading clickable"><!----><span class="title">《TypeScript 编程》</span><span class="arrow right"></span></button><!----></section></li><li><section class="sidebar-group"><button class="sidebar-heading clickable active"><!----><span class="title">《Vue.js 设计与实现》</span><span class="arrow down"></span></button><ul class="sidebar-links"><li><section class="sidebar-group"><button class="sidebar-heading clickable active"><!----><span class="title">第一篇 框架设计概览</span><span class="arrow down"></span></button><ul class="sidebar-links"><li><!--[--><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html" class="router-link-active router-link-exact-active nav-link active sidebar-link sidebar-page active" aria-label="第 1 章 权衡的艺术"><!---->第 1 章 权衡的艺术<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#_1-1-命令式和声明式" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1.1 命令式和声明式"><!---->1.1 命令式和声明式<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#_1-2-性能与可维护的权衡" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1.2 性能与可维护的权衡"><!---->1.2 性能与可维护的权衡<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#_1-3-虚拟-dom-的性能" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1.3 虚拟 DOM 的性能"><!---->1.3 虚拟 DOM 的性能<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#_1-4-运行时和编译时" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1.4 运行时和编译时"><!---->1.4 运行时和编译时<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#运行时" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="运行时"><!---->运行时<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html" class="nav-link sidebar-link sidebar-page" aria-label="第 2 章 框架设计的核心要素"><!---->第 2 章 框架设计的核心要素<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section></li></ul></section></li></ul><!--[--><!----><!--]--></aside><!--[--><main class="page" id="main-content"><!--[--><!----><nav class="breadcrumb disable"></nav><div class="page-title"><h1><!---->第 1 章 权衡的艺术</h1><div class="page-info"><!----><!----><!----><!----><!----><span class="reading-time-info" aria-label="阅读时间⌛" data-balloon-pos="down"><svg xmlns="http://www.w3.org/2000/svg" class="icon timer-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="timer icon"><path d="M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"></path></svg><span>大约 7 分钟</span><meta property="timeRequired" content="PT7M"></span></div><hr></div><div class="toc-place-holder"><aside id="toc"><div class="toc-header">此页内容</div><div class="toc-wrapper"><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#_1-1-命令式和声明式" class="router-link-active router-link-exact-active toc-link level2">1.1 命令式和声明式</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#_1-2-性能与可维护的权衡" class="router-link-active router-link-exact-active toc-link level2">1.2 性能与可维护的权衡</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#_1-3-虚拟-dom-的性能" class="router-link-active router-link-exact-active toc-link level2">1.3 虚拟 DOM 的性能</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#_1-4-运行时和编译时" class="router-link-active router-link-exact-active toc-link level2">1.4 运行时和编译时</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%201%20%E7%AB%A0.html#运行时" class="router-link-active router-link-exact-active toc-link level3">运行时</a></li><!----><!--]--></ul><!--]--></ul></div></aside></div><!----><div class="theme-hope-content"><h1 id="第-1-章-权衡的艺术" tabindex="-1"><a class="header-anchor" href="#第-1-章-权衡的艺术" aria-hidden="true">#</a> 第 1 章 权衡的艺术</h1><ul><li>命令式和声明式两种规范的差异，以及两者对框架设计的影响</li><li>虚拟 DOM 的性能状况</li><li>Vue.js 3.0 是<strong>运行时和编译时框架</strong></li></ul><h2 id="_1-1-命令式和声明式" tabindex="-1"><a class="header-anchor" href="#_1-1-命令式和声明式" aria-hidden="true">#</a> 1.1 命令式和声明式</h2><p>从范式来开，视图层框架分为命令式和声明式，两者各有优缺点。早期的 Jquery 是命令式框架。</p><p>举个 🌰：</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token doc-comment comment">/**
 * 获取 id 为 app 的 div 标签
 * 文本内容为 hello world
 * 绑定事件
 * 点击 alert(&#39;ok&#39;)
 */</span>

<span class="token comment">// Jquery 实现</span>
<span class="token function">$</span><span class="token punctuation">(</span><span class="token string">&#39;#app&#39;</span><span class="token punctuation">)</span> <span class="token comment">// 获取 标签</span>
  <span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token string">&#39;hello world&#39;</span><span class="token punctuation">)</span> <span class="token comment">// 设置内容</span>
  <span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">&#39;click&#39;</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">&#39;ok&#39;</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 绑定事件</span>


<span class="token comment">// JavaScript 实现</span>
<span class="token keyword">const</span> div <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">&#39;app&#39;</span><span class="token punctuation">)</span> <span class="token comment">// 获取 标签</span>
div<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">&#39;hellow world&#39;</span> <span class="token comment">// 设置内容</span>
div<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">&#39;click&#39;</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">&#39;ok&#39;</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 绑定事件</span>


<span class="token comment">// Vue 实现</span>
<span class="token operator">&lt;</span>div id<span class="token operator">=</span><span class="token string">&quot;app&quot;</span> @click<span class="token operator">=</span><span class="token string">&quot;() =&gt; alert(&#39;ok&#39;)&quot;</span> <span class="token operator">&gt;</span>hello world<span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>由上述 🌰 得出：</p><ul><li>命令式关注的是程序实现的<strong>过程</strong></li><li>声明式关注的是程序的<strong>最终结果</strong></li></ul><div class="custom-container tip"><p class="custom-container-title">提示</p><p>当然 Vue.js 不可能单单使用声明式，内部代码还是原生 JS 实现，也就是<strong>命令式</strong>，然而暴露给用户的则是<strong>声明式</strong>。所以 Vue.js 是一个<strong>命令式+声明式</strong>框架。</p></div><h2 id="_1-2-性能与可维护的权衡" tabindex="-1"><a class="header-anchor" href="#_1-2-性能与可维护的权衡" aria-hidden="true">#</a> 1.2 性能与可维护的权衡</h2><p><strong>声明式代码的性能不优于命令式代码的性能</strong></p><p>上述 🌰 来说，修改 div 标签内容为 hello vue3。</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code> <span class="token comment">// 命令式</span>
 div<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">&#39;hello vue3&#39;</span>
 
 <span class="token comment">// 声明式</span>
 <span class="token comment">// 修改之前</span>
 <span class="token operator">&lt;</span>div id<span class="token operator">=</span><span class="token string">&quot;app&quot;</span> @click<span class="token operator">=</span><span class="token string">&quot;() =&gt; alert(&#39;ok&#39;)&quot;</span> <span class="token operator">&gt;</span>hello world<span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
 <span class="token comment">// 修改之后</span>
 <span class="token operator">&lt;</span>div id<span class="token operator">=</span><span class="token string">&quot;app&quot;</span> @click<span class="token operator">=</span><span class="token string">&quot;() =&gt; alert(&#39;ok&#39;)&quot;</span> <span class="token operator">&gt;</span>hello vue3<span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li><p>命令式代码：明确结果，只要调用相关命令操作即可</p></li><li><p>声明式代码：首先要找到前后的差异，然后再去更新最终使用的还是命令是代码</p></li></ul><p>如果把直接修改的性能定义为 A，找出差异的代码性能定义为 B，可得出：</p><ul><li>命令式代码的性能消耗：A</li><li>声明式代码的性能消耗：B + A <ul><li><strong>声明式代码的性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗</strong></li></ul></li></ul><p>由上看出，声明式的性能消耗会比命令式的性能消耗多出找出差异的性能消耗。<strong>框架本身是封装了命令式代码实现面向用户的声明式。</strong></p><p>选择生命是的原因：声明式代码用户维护性更强，如果使用命令式我们需要<strong>维护整个代码过程</strong>。</p><h2 id="_1-3-虚拟-dom-的性能" tabindex="-1"><a class="header-anchor" href="#_1-3-虚拟-dom-的性能" aria-hidden="true">#</a> 1.3 虚拟 DOM 的性能</h2><p>上小节说到 <strong>声明式代码的性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗</strong>。如果将找出差异的性能降到最低，接近于 0 的话，那么声明式性能消耗就会约等于命令式性能消耗。</p><p>虚拟 DOM 就是为了<strong>最小化找出差异的性能消耗</strong>。</p><p><img src="/doc/assets/1.3-js-test.e606c5c4.png" alt="js"> <img src="/doc/assets/1.3-dom-test.b0668b05.png" alt="dom"></p><p>由上图看出，纯 JS 的操作比操作 DOM 的使用时间要快的多。</p><p>可以使用公式表达：<strong>innerHtml 创建页面性能 = HTML 字符串拼接的计算量 + innerHTML 的 DOM 计算量</strong></p><p>虚拟 DOM 创建页面过程分为两步：</p><ol><li>创建 JS 对象，理解为真实 DOM 的描述</li><li>递归遍历虚拟 DOM 树并创建真实 DOM</li></ol><p>用公式表达：<strong>虚拟 DOM 创建页面 = 创建 JS 对象的计算量 + 创建真实 DOM 的计算量</strong></p><table><thead><tr><th>运算方式</th><th>虚拟 DOM</th><th>innerHTML</th></tr></thead><tbody><tr><td>纯 JS 运算</td><td>创建 JS 对象(VNode)</td><td>渲染 HTML 字符串</td></tr><tr><td>DOM 运算</td><td>新建所有 DOM</td><td>新建所有 DOM</td></tr></tbody></table><p>由上表格可以看出其实新建页面时两种运算方式最终都需要新建所有 DOM。在同一个数量级，可能差异会很小。</p><p>如果是更新呢？</p><p>使用 innerHTML 更新页面的过程是<strong>重新构建 HTML 字符串，再更新设置 DOM 元素的 innerHTML 属性</strong>， 哪怕更新一个字也需要重新设置 innerHTML 属性。也就等价于<strong>销毁所有旧的 DOM 元素，再全量创建新的 DOM。</strong></p><table><thead><tr><th>运算方式</th><th>虚拟 DOM</th><th>innerHTML</th></tr></thead><tbody><tr><td>纯 JS 运算</td><td>创建新的 JS 对象(VNode) + Diff</td><td>渲染 HTML 字符串</td></tr><tr><td>DOM 运算</td><td>必要的 DOM 更新</td><td>销毁所有旧 DOM，新建所有新的 DOM</td></tr></tbody></table><p>由上对比，在创建虚拟 DOM 在 JS 层面的运算要比创建页面时多出一个 Diff 的性能消耗，因为是 JS 层面的运算，产生的数量差异不是很大。</p><p>DOM 层面运算，虚拟 DOM 在更新的时候只更新有变化的元素，但 innerHTML 需要全量更新。</p><p>更新页面时，影响虚拟 DOM 的性能因素与影响 innerHTML 的性能因素不同。虚拟 DOM 无论页面多大，只会更新变化的内容。而 innerHTML 来说，页面越大，意味着更新时的性能消耗越大。</p><p>加上性能因素对比：</p><table><thead><tr><th>运算方式</th><th>虚拟 DOM</th><th>innerHTML</th></tr></thead><tbody><tr><td>纯 JS 运算</td><td>创建新的 JS 对象(VNode) + Diff</td><td>渲染 HTML 字符串</td></tr><tr><td>DOM 运算</td><td>必要的 DOM 更新</td><td>销毁所有旧 DOM，新建所有新的 DOM</td></tr><tr><td>性能因素</td><td>与数据变化量相关</td><td>与模板大小相关</td></tr></tbody></table><p>innerHTML、虚拟 DOM 、原生 JS (createElement) 性能由低到高对比：</p><ul><li>innerHTML <ul><li>心智负担中等</li><li>性能差</li></ul></li><li>虚拟 DOM <ul><li>心智负担小</li><li>可维护性强</li><li>性能中等</li></ul></li><li>原生 JS <ul><li>心智负担大</li><li>可维护性差</li><li>性能高</li></ul></li></ul><p>按照心智负担、可维护性和性能分析。其中原生 DOM 操作心智负担最大，因为要手动创建、删除、修改大量的 DOM 元素 。但其性能却是最佳的，另外可维护性却很差。</p><p>innerHTML 来说，编写页面的过程有一部分是通过拼接 HTML 字符串来实现，有些接近声明式的意思，但拼接字符串也是 有一定的心智负担的。对于事件绑定还是需要 JS 来处理。如果模板字符串很大，则其更新页面的性能很低，尤其在少量更新 的情况下。</p><p>虚拟 DOM，是声明式的，心智负担很低，可维护性强。相对于原生 JS 的性能有一定的差距，可是在心智负担和可维护性的 前提下还是相当不错的。</p><h2 id="_1-4-运行时和编译时" tabindex="-1"><a class="header-anchor" href="#_1-4-运行时和编译时" aria-hidden="true">#</a> 1.4 运行时和编译时</h2><p>框架类型：纯运行时、运行时+编译时或纯编译时</p><h3 id="运行时" tabindex="-1"><a class="header-anchor" href="#运行时" aria-hidden="true">#</a> 运行时</h3><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">tag</span><span class="token operator">:</span><span class="token string">&#39;div&#39;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">children</span><span class="token operator">:</span><span class="token punctuation">[</span>
    <span class="token punctuation">{</span><span class="token literal-property property">tag</span><span class="token operator">:</span><span class="token string">&#39;span&#39;</span><span class="token punctuation">,</span><span class="token literal-property property">children</span><span class="token operator">:</span><span class="token string">&#39;hello world!&#39;</span><span class="token punctuation">}</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">Render</span><span class="token punctuation">(</span><span class="token parameter">obj<span class="token punctuation">,</span>root</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> el <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>tag<span class="token punctuation">)</span>
  <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> obj<span class="token punctuation">.</span>children <span class="token operator">===</span> <span class="token string">&#39;string&#39;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 
    <span class="token keyword">const</span> text <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createTextNode</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>children<span class="token punctuation">)</span>
    el<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">{</span>obj<span class="token punctuation">.</span>children<span class="token punctuation">}</span> <span class="token punctuation">{</span>
    obj<span class="token punctuation">.</span>children<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">child</span><span class="token operator">=&gt;</span> <span class="token function">Render</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>children<span class="token punctuation">,</span>el<span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  root<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>el<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token function">Render</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span>document<span class="token punctuation">.</span>body<span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码实现一个简单地 Render 函数，根据树形结构的数据对象递归的将数据渲染成 DOM 元素。 不能由用户自定义模板，只能根据规定的 DOM 树型结构来渲染。上述代码是一个<strong>纯运行时</strong>的函数</p><p>如果页面很复杂的话，用户创建树形结构会很麻烦，而且不直观。能否直接使用 HTML 标签描述树结构呢？</p><p>此刻需要借助于编译模式，将 HTML 编译成树形结构。</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code>
<span class="token keyword">const</span> html <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">
    &lt;div&gt;
      &lt;span&gt; hello world &lt;/span&gt;
    &lt;/div&gt;
</span><span class="token template-punctuation string">`</span></span>

<span class="token keyword">function</span> <span class="token function">Compiler</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token comment">/*
...
*/</span><span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Compiler 函数就是用于将 HTML 编译成树形结构。用户需要先调用 Compiler 函数，然后再调用 Render 函数。 Render + Compiler 就形成一个Compiler的代码。</p><p>如果 Compiler 函数内部直接将 HTML 字符串编译成命令式代码的话，我们直接调用 Compiler 函数即可将 DOM 渲染。此刻就变成了<strong>纯编译时</strong>。</p><p>优缺点对比：</p><ul><li>纯运行时，没有编译过程，无法分析用户提供的内容。加入编译步骤，可以分析用户提供的内容，分析哪些内容可能会改变，那些不变。 在编译时提取这些信息后传递给 Render 函数，Render 函数做进一步优化。</li><li>纯编译时，可以分析用户内容，不需要任何运行时，直接编译成可执行的 JS 代码，性能会更好，但是缺少灵活性。即用户提供的内容编译后才能使用。</li></ul></div><!----><footer class="page-meta"><!----><!----><!----></footer><nav class="page-nav"><!----><a href="/doc/readbook/%E3%80%8AVue.js%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B/%E7%AC%AC%E4%B8%80%E7%AF%87/%E7%AC%AC%202%20%E7%AB%A0.html" class="nav-link next" aria-label="第 2 章 框架设计的核心要素"><div class="hint">下一页<span class="arrow right"></span></div><div class="link">第 2 章 框架设计的核心要素<!----></div></a></nav><!----><!----><!--]--></main><!--]--><!----><!--]--></div><!--]--><!----><!--]--></div>
    <script type="module" src="/doc/assets/app.41440b49.js" defer></script>
  </body>
</html>
